package com.gitee.wsl.compose.chart.xy.bar.stack.model

import com.gitee.wsl.compose.chart.ChartAdapter
import com.gitee.wsl.compose.chart.xy.bar.model.BarEntry
import com.gitee.wsl.compose.chart.xy.bar.model.BarPosition
import com.gitee.wsl.compose.chart.xy.model.ListPointEntry

/**
 * Represents a set of data points for a [StackedVerticalBarPlot].
 */
 interface BarStackedEntry<X, Y> : ListPointEntry<X,Y> {
    /**
     * The x-axis value of the entry.
     */
     //val x: X

    /**
     * The value for the y-axis Origin, which is the bottom of the lowest bar in the stack.
     */
     val yOrigin: Y

    /**
     * The y-axis coordinate of the top of each bar in the stack, where lower indices are for bars lower in the stack.
     */
     //val y: List<Y>
}

fun<X, Y> BarStackedEntry(x: X, yOrigin: Y, y: List<Y>):BarStackedEntry<X, Y> = DefaultBarStackedEntry(x, yOrigin, y)

fun<X, Y> BarStackedEntry( yOrigin: Y, listPointEntry: ListPointEntry<X,Y>):BarStackedEntry<X, Y> = DefaultBarStackedEntry( yOrigin, listPointEntry)

fun<X, Y,E:ListPointEntry<X, Y>> List<E>.toBarStackedEntry( offsetFun:( E )-> Y ) = map{ BarStackedEntry(offsetFun(it),it) }

internal data class DefaultBarStackedEntry<X, Y>(
    override val x: X,
    override val yOrigin: Y,
    override val y: List<Y>,
) : BarStackedEntry<X, Y>{
    constructor(yOrigin: Y,listPointEntry: ListPointEntry<X,Y>):this(listPointEntry.x,yOrigin,listPointEntry.y)
}

class StackToBarEntryAdapter<X, Y>(
    val data: List<BarStackedEntry<X, Y>>,
    val barIndex: Int
) : AbstractList<BarEntry<X, Y>>() {

    override val size: Int = data.size

    override fun get(index: Int): BarEntry<X, Y> {
        return object : BarEntry<X, Y> {
            override val x: X = data[index].x
            override val y: BarPosition<Y>
                get() = BarPositionAdapter(data[index], barIndex)
        }
    }

class BarPositionAdapter<X, Y>(
    entry: BarStackedEntry<X, Y>,
    barIndex: Int
) : BarPosition<Y> {

        override val yMin: Y = if (barIndex == 0) {
            entry.yOrigin
        } else {
            // barIndex can be greater than entry.y.lastIndex if some other x-axis entries had more
            // bars to display in the stack
            entry.y[(barIndex - 1).coerceAtMost(entry.y.lastIndex)]
        }

        override val yMax: Y = entry.y[barIndex.coerceAtMost(entry.y.lastIndex)]
    }
}

fun <X, Y> ChartAdapter.Companion.stackBar(builder:  StackBarAdapterBuilder<X, Y>.() -> Unit):StackToBarEntryAdapter<X, Y>{
    val data: MutableList<BarStackedEntry<X, Y>> = mutableListOf()
    var barIndex: Int = 0
    object:StackBarAdapterBuilder<X, Y>{
        override fun Pair<X, Y>.of(vararg yValue: Y) {
           data.add(BarStackedEntry(first,second,yValue.toList()))
        }
        override fun onBarIndex(index: Int) {
            barIndex = index
        }
    }.builder()
    return StackToBarEntryAdapter(data,barIndex)
}

interface StackBarAdapterBuilder<X, Y>{
    fun onBarIndex(index:Int)
    fun Pair<X,Y>.of(vararg yValue:Y)
}